#ifndef QP_QTIME_EVT_HPP
#define QP_QTIME_EVT_HPP
#include <memory>
#include "QEvt.hpp"

namespace QP {
using QTimeEvtCtr = std::uint32_t;

class QActive;

class QTimeEvt final : public QEvt,
                       public std::enable_shared_from_this<QTimeEvt> {
 public:
  //! The Time Event constructor.
  QTimeEvt(QActive * const act, int s) noexcept;
  std::shared_ptr<QTimeEvt> getSharedFromThis() { return shared_from_this(); }

  /// \brief Arm a one-shot time event for direct event posting.
  ///
  /// Arms a time event to fire in \a nTicks clock ticks (one-shot time
  /// event). The time event gets directly posted (using the FIFO policy)
  /// into the event queue of the active object \a act.
  ///
  /// After posting, the time event gets automatically disarmed and can be
  /// reused for a one-shot or periodic timeout requests.
  ///
  /// A one-shot time event can be disarmed at any time by calling the
  /// QTimeEvt::disarm() function. Also, a one-shot time event can be
  /// re-armed to fire in a different number of clock ticks by calling the
  /// QTimeEvt::rearm() function.
  ///
  /// The following example shows how to arm a one-shot time event from a
  /// state machine of an active object:
  /// \include qf_state.cpp
  void postIn(QTimeEvtCtr const nTicks) {
    m_interval = static_cast<QTimeEvtCtr>(0);
    arm_(nTicks);
  }

  /// \brief Arm a periodic time event for direct event posting.
  ///
  /// Arms a time event to fire every \a nTicks clock ticks (periodic time
  /// event). The time event gets directly posted (using the FIFO policy)
  /// into the event queue of the active object \a act.
  ///
  /// After posting, the time event gets automatically re-armed to fire
  /// again in the specified \a nTicks clock ticks.
  ///
  /// A periodic time event can be disarmed only by calling the
  /// QTimeEvt::disarm() function. After disarming, the time event can be
  /// reused for a one-shot or periodic timeout requests.
  ///
  /// \note An attempt to reuse (arm again) a running periodic time event
  /// raises an assertion.
  ///
  /// Also, a periodic time event can be re-armed to shorten or extend the
  /// current period by calling the QTimeEvt_rearm() function. After
  /// adjusting the current period, the periodic time event goes back
  /// timing out at the original rate.
  void postEvery(QTimeEvtCtr const nTicks) {
    m_interval = nTicks;
    arm_(nTicks);
  }

  /// \brief Disarm a time event.
  ///
  /// The time event gets disarmed and can be reused. The function
  /// returns true if the time event was truly disarmed, that is, it
  /// was running. The return of false means that the time event was
  /// not truly disarmed because it was not running. The 'false' return is
  /// only possible for one-shot time events that have been automatically
  /// disarmed upon expiration. In this case the 'false' return means that
  /// the time event has already been posted or published and should be
  /// expected in the active object's state machine.
  bool disarm(void) noexcept;

  /// \brief Rearm a time event.
  ///
  /// The time event gets rearmed with a new number of clock ticks
  /// \a nTicks. This facility can be used to prevent a one-shot time event
  /// from expiring (e.g., a watchdog time event), or to adjusts the
  /// current period of a periodic time event. Rearming a periodic timer
  /// leaves the interval unchanged and is a convenient method to adjust
  /// the phasing of the periodic time event.
  ///
  /// The function returns true if the time event was running as it
  /// was re-armed. The return of false means that the time event was
  /// not truly rearmed because it was not running. The 'false' return is
  /// only possible for one-shot time events that have been automatically
  /// disarmed upon expiration. In this case the 'false' return means that
  /// the time event has already been posted or published and should be
  /// expected in the active object's state machine.
  bool rearm(QTimeEvtCtr const nTicks) noexcept;

  /// \brief Get the current value of the down-counter of a time event.
  ///
  /// If the time event is armed, the function returns the current value of
  /// the down-counter of the given time event. If the time event is not
  /// armed, the function returns 0.
  ///
  /// /note The function is thread-safe.
  QTimeEvtCtr ctr(void) const noexcept;

 private:
  //! private copy constructor to disallow copying of QTimeEvts
  QTimeEvt(QTimeEvt const &) = delete;
  //! private assignment operator to disallow assigning of QTimeEvts
  QTimeEvt &operator=(QTimeEvt const &) = delete;

  /// \brief Arm a time event (internal function to be used through macros
  /// only).
  ///
  /// \sa QTimeEvt::postIn(), QTimeEvt::postEvery(),
  /// \sa QTimeEvt::publishIn(), QTimeEvt::publishEvery()
  void arm_(QTimeEvtCtr const nTicks) noexcept;

  friend class QF;

 private:
  /// the active object that receives the time events.
  QActive *m_act;

  /// the internal down-counter of the time event. The down-counter
  /// is decremented by 1 in every QF_tick() invocation. The time event
  /// fires (gets posted or published) when the down-counter reaches zero.
  QTimeEvtCtr m_ctr;

  /// the interval for the periodic time event (zero for the one-shot
  /// time event). The value of the interval is re-loaded to the internal
  /// down-counter when the time event expires, so that the time event
  /// keeps timing out periodically.
  QTimeEvtCtr m_interval;
};
}

#endif  // QP_QTIME_EVT_HPP
